#pragma once
#include <vector>
#include "CG_skel_w_MFC.h"
#include "vec.h"
#include "mat.h"
#include "GL/glew.h"

//the PEn is simply an  immutable object...
class Pen {
private:
	GLfloat m_red;
	GLfloat m_green;
	GLfloat m_blue;
public:
	Pen(GLfloat r=1, GLfloat g=1, GLfloat b=1):m_red(r),m_green(g),m_blue(b) {}
	inline GLfloat getRed() { return m_red; }
	inline GLfloat getBlue() { return m_blue; }
	inline GLfloat getGreen() { return m_green; }
};


using namespace std;
class Renderer
{
	float *m_outBuffer; // 3*width*height
	float *m_zbuffer; // width*height

	int m_width, m_height;
	Pen m_regularPen;
	Pen m_activePen;
	Pen * m_currentPen;	
	bool flagDrawNormals;
	bool flagDrawVertexNormals;
	bool flagDrawBoundingBox;

	mat4 m_projectionMatrix;
	mat4 m_cameraMatrix;
	
	mat4 m_objectMatrix;
	mat3 m_normalMatrix;

	GLfloat m_normalLength;

	void TransformToViewport(vec2 & v);
	void CreateBuffers(int width, int height);
	void CreateLocalBuffer();

	void DrawSingleTriangle(const vec4 & v1,const vec4 & v2,const vec4 & v3);
	//void drawNormalForTriangle(vec4& v1,vec4& v2, vec4& v3);//TODO add const
	void drawVertexNormal(const vec3& v,const vec3& n, GLfloat modelDiameter);

	vec2 TransformVertex(const vec4 & v);
	vec2 ProjectVertex(const vec4 & v);
	vec4 Transform2NSC(const vec4 & v);
	vec2 Transform2Screen(const vec4 & v);
	//////////////////////////////
	// openGL stuff. Don't touch.

	GLuint gScreenTex;
	GLuint gScreenVtc;
	void CreateOpenGLBuffer();
	void InitOpenGLRendering();

	vec3 calculateNormal(const vec4& v1,const vec4& v2,const vec4& v3);
	vec3 calculateTriangleCenter(const vec4& v1,const vec4& v2,const vec4& v3);
	//////////////////////////////

public:
	Renderer();
	Renderer(int width, int height);
	~Renderer(void);
	void Init();
	void DrawLine(const vec2 & p1, const vec2 & p2);
	// why vec3 and not vec4? should we first translate to homogenous coordinates? Well I assume yes...
	void DrawTriangles(const vector<vec3>* vertices, const vector<vec3>* normals=NULL);
	void SetCameraTransform(const mat4& cTransform);
	void SetProjection(const mat4& projection);
	void SetObjectMatrices(const mat4& oTransform, const mat3& nTransform);
	void SwapBuffers();
	void ClearColorBuffer();
	void ClearDepthBuffer();
	void SetDemoBuffer();
	void PlotPixel(const int & x, const int & y);
	void ClearOutBuffer() { for (int i=0; i< m_height*m_width*3; i++) m_outBuffer[i] = 0; }
	void DrawPlusAtVertex(const vec4 & v);
	inline void TakeRegularPen() { m_currentPen = &m_regularPen; }
	inline void TakeActivePen() { m_currentPen = &m_activePen; }
	void toggleFaceNormals(){	flagDrawNormals = !flagDrawNormals;	}
	void toggleVertexNormals(){	flagDrawVertexNormals = !flagDrawVertexNormals; }
	void toggleBoundingBox(){ flagDrawBoundingBox = !flagDrawBoundingBox; };
	void drawNormalForTriangle(const vec4& v1,const vec4& v2,const vec4& v3, GLfloat modelDiameter);
	void drawVertexNormals(const vector<vec3> * vertices, const vector<vec3> * normals);//,GLfloat modelDiameter);
	void drawBoundingBox(vec3 boundingBoxMin, vec3 boundingBoxMax);
	void setNormalLength( const GLfloat & nLength) { m_normalLength = nLength; }
	void ReCreateBuffers(int width, int height);
	inline void SetActivePen(const Pen & p) { this->m_activePen = p; }
	inline void SetPassivePen(const Pen & p) {this->m_regularPen = p; }
};
